package com.xiaoping.base;

/**
 * 对于并查集而言
 * 需要支持两个动作
 * 1.union 2.find
 * 回答一个问题
 * 1.isConnected
 */
public class UnionFind {

    private int[] parent;   // 标注当前元素的父节点的位置

    private int[] rank;     // 标注当前元素的层级数

    private int size;       // 并查集中的元素个数

    public UnionFind (int size){
        this.size = size;

        parent = new int[size];
        rank = new int[size];

        init();
    }

    private void init() {
        for (int i = 0; i < size; i++) {
            // 初始化时所有的节点的父节点指向本身，所有的元素层级均为 1
            parent[i] = i;
            rank[i] = 1;
        }
    }


    /**
     * 寻找当前节点的根节点元素
     * @param target
     * @return
     */
    public int find(int target) {
        if(target >= size)
            throw new ArrayIndexOutOfBoundsException();
        if(target == parent[target])
            return target;
        return find(parent[target]);
    }

    /**
     * 连接两个元素
     * @param p
     * @param q
     */
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        if(pRoot == qRoot)
            return;
        if(rank[pRoot] > rank[qRoot]) { // p 所在的树的高度比 q 所在输的高度高，这时应该让 q 的根节点元素连接到 p 的根节点元素
            parent[qRoot] = pRoot; // 此时树的高度不变
        } else if(rank[pRoot] < rank[qRoot]) {
            parent[pRoot] = qRoot; // 此时树的高度不变
        } else {
            parent[pRoot] = qRoot; // 此时树的高度 +1
            rank[qRoot] += 1;
        }
    }

    /**
     * 判断两个节点是否连接
     * @param p
     * @param q
     * @return
     */
    public boolean isConnected(int p, int q) {
        // 如果两个节点的根节点一致则表示这两个节点是相连接的
        return find(p) == find(q);
    }

}
